﻿using Grpc.Core;
using Grpc.Net.Client;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;

namespace CommonTools
{
    public class GrpcClient
    {
        private readonly Uri uri;
        public GrpcClient(Uri address)
        {
            this.uri = address ?? throw new ArgumentNullException("address can not be null");
        }

        public GrpcClient(string address) : this(new Uri(address))
        {

        }



        /// <summary>
        /// 发送grpc请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数泛型类型</typeparam>
        /// <typeparam name="TReply">响应结果泛型类型</typeparam>
        /// <typeparam name="TClient">grpc请求客户端泛型类型</typeparam>
        /// <param name="buildClient">构建请求客户端</param>
        /// <param name="request">请求参数</param>
        /// <param name="call">grpc请求方法</param>
        /// <param name="action">请求头设置</param>
        /// <param name="errCallback">异常回调</param>
        /// <returns></returns>
        public async Task<TReply> SendGrpcRequestAsync<TRequest, TReply, TClient>(Func<GrpcChannel, TClient> buildClient, TRequest request, Func<TClient, TRequest, Metadata, AsyncUnaryCall<TReply>> call, Action<Metadata> setHeader = null, Action<Exception> errCallback = null)
            where TClient : ClientBase<TClient> where TReply : class, new()
        {
            if (buildClient == null)
            {
                throw new ArgumentNullException("buildClient can not be null");
            }

            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true);

            GrpcChannel channel = null;
            try
            {
                channel = GrpcChannel.ForAddress(uri);
                var client = buildClient(channel);
                var metaData = new Metadata();
                setHeader?.Invoke(metaData);

                return await call.Invoke(client, request, metaData);
            }
            catch (Exception ex)
            {
                errCallback?.Invoke(ex);

                return await Task.FromResult(new TReply());
            }
            finally
            {
                channel?.Dispose();
            }
        }



        /// <summary>
        /// 发送grpc请求
        /// </summary>
        /// <typeparam name="TRequest">请求参数泛型类</typeparam>
        /// <typeparam name="TReply">响应泛型类</typeparam>
        /// <typeparam name="TClient">grpc客户端泛型类</typeparam>
        /// <param name="buildClient">创建grpc客户端</param>
        /// <param name="request">请求参数</param>
        /// <param name="call">grpc请求方法</param>
        /// <param name="errCallback">异常回调</param>
        /// <returns></returns>
        public async Task<TReply> SendGrpcRequestAsync<TRequest, TReply, TClient>(Func<GrpcChannel, TClient> buildClient, TRequest request, Func<TClient, TRequest, AsyncUnaryCall<TReply>> call, Action<Exception> errCallback = null)
            where TClient : ClientBase<TClient> where TReply : class, new()
        {
            if (buildClient == null)
            {
                throw new ArgumentNullException("buildClient can not be null");
            }

            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
            AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2Support", true);

            GrpcChannel channel = null;
            try
            {
                channel = GrpcChannel.ForAddress(uri);
                var client = buildClient(channel);

                return await call.Invoke(client, request);
            }
            catch (Exception ex)
            {
                errCallback?.Invoke(ex);

                return await Task.FromResult(new TReply());
            }
            finally
            {
                channel?.Dispose();
            }
        }
    }
}
